﻿namespace Harness
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using mshtml;

    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("Harness")]
    [Guid("F03B9C9D-5178-4887-8A5D-80CF72CBFF0A")]
    public class Harness : IHarness
    {
        #region Constants

        private const int DefaultTimeoutSeconds = 60 * 3;

        #endregion

        #region Fields

        private string baseUrl;

        private int timeoutSeconds = DefaultTimeoutSeconds;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the Harness class.
        /// </summary>
        public Harness()
        {
            this.Version = Assembly.GetExecutingAssembly().GetName().Version;
            this.TimeoutSeconds = DefaultTimeoutSeconds;
        }

        #endregion

        #region Destructors
        #endregion

        #region Delegates
        #endregion

        #region Events

        public event ElementHighlightEventHandler ElementHighlighted;

        #endregion

        #region Enums
        #endregion

        #region Interfaces
        #endregion

        #region Properties

        /// <summary>
        /// Gets Harness version
        /// </summary>
        public Version Version
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets timeout seconds
        /// </summary>
        public int TimeoutSeconds
        {
            get
            {
                return this.timeoutSeconds;
            }

            set
            {
                this.timeoutSeconds = value;
                IE.TimeoutSeconds = value;
            }
        }

        /// <summary>
        /// Gets or sets base url
        /// </summary>
        public string BaseUrl
        {
            get
            {
                return this.baseUrl;
            }

            set
            {
                if (!string.IsNullOrWhiteSpace(value))
                {
                    if (!value.StartsWith("http://") && !value.StartsWith("https://"))
                    {
                        throw new ArgumentException("not http:// or https://", value);
                    }
                }

                this.baseUrl = value;
            }
        }

        #endregion

        #region Indexers
        #endregion

        #region Public Methods

        /// <summary>
        /// execute command
        /// </summary>
        /// <param name="cmd">command name</param>
        /// <param name="arg1">argument 1 (optional)</param>
        /// <param name="arg2">argument 2 (optional)</param>
        /// <param name="arg3">argument 3 (optional)</param>
        /// <param name="arg4">argument 4 (optional)</param>
        /// <param name="arg5">argument 5 (optional)</param>
        /// <returns>result object</returns>
        public dynamic Command(string cmd, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null)
        {
            if (string.IsNullOrWhiteSpace(cmd))
            {
                throw new ArgumentNullException("cmd");
            }

            ICommand command = CommandFactory.GetCommand(cmd);

            object[] args = { arg1, arg2, arg3, arg4, arg5 };
            command.SyntaxCheck(this, args);

            dynamic result = command.Execute(this, args);
            IE.WaitForReadyStateInteractiveOrComplete();

            return result;
        }

        public void SyntaxCheck(string cmd, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null)
        {
            ICommand command = CommandFactory.GetCommand(cmd);

            object[] args = { arg1, arg2, arg3, arg4, arg5 };
            command.SyntaxCheck(this, args);
        }

        /// <summary>
        /// Gets the absolute URL supplemented with BaseUrl property
        /// </summary>
        /// <param name="url">A relative or absolute URL</param>
        /// <returns>absolute URL</returns>
        public string GetAbsoluteUrl(string url)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                throw new ArgumentNullException("url");
            }

            if (string.IsNullOrWhiteSpace(this.BaseUrl))
            {
                return url;
            }

            if (url.StartsWith("http://") || url.StartsWith("https://"))
            {
                return url;
            }

            Uri u1 = new Uri(this.BaseUrl);
            Uri u2 = new Uri(u1, url);

            return u2.AbsoluteUri;
        }

        public void HighlightElement(IHTMLElement element, string cmd = "")
        {
            if (null == element)
            {
                return;
            }

            Rectangle rect = IE.Element.Highlight(element);
            this.OnElementHighlighted(element, rect, cmd);
        }

        #endregion

        #region Private Methods

        private void OnElementHighlighted(IHTMLElement element, Rectangle rect, string cmd)
        {
            if (null != this.ElementHighlighted)
            {
                this.ElementHighlighted(this, element, rect, cmd);
            }
        }

        #endregion

        #region Structs
        #endregion

        #region Classes
        #endregion
    }
}
